package com.caimiao.miao.library.usb.accessory;

import android.os.ParcelFileDescriptor;

import com.caimiao.miao.library.usb.interfaces.IUsbControl;
import com.caimiao.miao.library.usb.interfaces.IUsbTransfer;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

/**
 * USB传输接口层
 */
public class UsbTransfer implements IUsbTransfer {
    public static final String TAG = "UsbTransfer";
    protected ParcelFileDescriptor mFileDescriptor;
    //发送
    protected FileOutputStream mOutputStream;
    protected volatile byte[] mWriteUsbData;//线程安全
    //接收
    protected FileInputStream mInputStream;
    protected ReadData mReadData;
    protected Disposable mDisposable;
    protected IUsbControl mUsbControl;

    protected class ReadData {
        private int readCount;
        private byte[] usbData;

        public ReadData() {
            readCount = 0;
            usbData = new byte[1024];
        }

        public int getReadCount() {
            return readCount;
        }

        public void setReadCount(int readCount) {
            this.readCount = readCount;
        }

        public byte[] getUsbData() {
            return usbData;
        }

        public void setUsbData(byte[] usbData) {
            this.usbData = usbData;
        }
    }

    public UsbTransfer(ParcelFileDescriptor mFileDescriptor, FileOutputStream mOutputStream, FileInputStream mInputStream, IUsbControl mUsbControl) {
        this.mFileDescriptor = mFileDescriptor;
        this.mOutputStream = mOutputStream;
        this.mInputStream = mInputStream;
        this.mUsbControl = mUsbControl;
        /*shall we start a thread here or what*/
        mWriteUsbData = new byte[256];
        mReadData = new ReadData();
        mDisposable = startReadThread();
    }

    @Override
    public void close(boolean isConfig) {
        if (!isConfig) {
            setConfig(9600, (byte) 1, (byte) 8, (byte) 0, (byte) 0);  // 发送默认设置数据进行配置
            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
        }
        if (mDisposable != null) mDisposable.dispose();
        mDisposable = null;
        mWriteUsbData[0] = 0;  // 为输入流发送虚拟数据
        sendPacket(1);
        try {
            Thread.sleep(10);
        } catch (Exception e) {
        }
        Close();
    }

    private void Close() {
        try {
            if (mFileDescriptor != null)
                mFileDescriptor.close();

        } catch (IOException e) {
        }

        try {
            if (mInputStream != null)
                mInputStream.close();
        } catch (IOException e) {
        }

        try {
            if (mOutputStream != null)
                mOutputStream.close();

        } catch (IOException e) {
        }
        mFileDescriptor = null;
        mInputStream = null;
        mOutputStream = null;
    }

    /**************************************发送*****************************************************/
    @Override
    public void setConfig(int baud, byte dataBits, byte stopBits,
                          byte parity, byte flowControl) {

        /*准备波特率缓冲器*/
        mWriteUsbData[0] = (byte) baud;
        mWriteUsbData[1] = (byte) (baud >> 8);
        mWriteUsbData[2] = (byte) (baud >> 16);
        mWriteUsbData[3] = (byte) (baud >> 24);

        /*数据bits*/
        mWriteUsbData[4] = dataBits;
        /*停止 bits*/
        mWriteUsbData[5] = stopBits;
        /*parity*/
        mWriteUsbData[6] = parity;
        /*flow control*/
        mWriteUsbData[7] = flowControl;

        /*send the UART configuration packet*/
        sendPacket(8);
    }

    @Override
    public byte sendData(char[] value) {
        return sendData(value == null ? 0 : value.length, value);
    }

    private byte sendData(int numBytes, char[] buffer) {
        byte status = 0x00; /*success by default*/
        /*
         * if num bytes are more than maximum limit
         */
        if (numBytes < 1) {
            /*return the mStatus with the error in the command*/
            return status;
        }

        /*check for maximum limit*/
        if (numBytes > 256) {
            numBytes = 256;
        }

        /*prepare the packet to be sent*/
        for (int count = 0; count < numBytes; count++) {
            mWriteUsbData[count] = (byte) buffer[count];
        }

        if (numBytes != 64) {
            sendPacket(numBytes);
        } else {
            byte temp = mWriteUsbData[63];
            sendPacket(63);
            mWriteUsbData[0] = temp;
            sendPacket(1);
        }

        return status;
    }

    /**
     * 在USB上发包
     *
     * @param numBytes 发送长度
     */
    protected void sendPacket(int numBytes) {
        try {
            if (mOutputStream != null) {
                mOutputStream.write(mWriteUsbData, 0, numBytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**************************************接收*****************************************************/

    protected Disposable startReadThread() {
        return Observable.interval(0, 50, TimeUnit.MILLISECONDS)
                .flatMap(new Function<Long, ObservableSource<ReadData>>() {
                    @Override
                    public ObservableSource<ReadData> apply(Long aLong) throws Exception {
                        return Observable.create(new ObservableOnSubscribe<ReadData>() {
                            @Override
                            public void subscribe(ObservableEmitter<ReadData> emitter) throws Exception {
                                mReadData.readCount = mInputStream.read(mReadData.usbData, 0, 1024);
                                emitter.onNext(mReadData);
                            }
                        });
                    }
                })
                .subscribeOn(Schedulers.newThread())
                .subscribe(new Consumer<ReadData>() {
                    @Override
                    public void accept(ReadData readData) throws Exception {
                        if (readData.readCount > 0)
                            mUsbControl.setReadData(readData.readCount, readData.usbData);
                    }
                });
    }
}
